﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ITAP.Core.Calculations;
using ITAP.Core.Modelling.Primitives;
using ITAP.ContourBuilder.Constraints;
using ITAP.ContourBuilder.Components.Constraints;
using System.Drawing;
using ITAP.ContourBuilder.Components.Dimensions;
using ITAP.ContourBuilder.Utils;
using ITAP.Core.Modelling;
using ITAP.Core.Utils;

namespace ITAP.ContourBuilder.Components
{
    public class ContourElementFactory
    {
        public static ContourModel CreateModel()
        {
            ContourModel model = new ContourModel();
            model.PreUpdatePriority.Add(typeof(ContourConnector));
            model.PreUpdatePriority.Add(typeof(ContourElement));
            model.PreUpdatePriority.Add(typeof(ContourElementDimension));
            model.PreUpdatePriority.Add(typeof(ArcHeightDimension));
            model.PreUpdatePriority.Add(typeof(ArcRadiusDimension));
            model.PreUpdatePriority.Add(typeof(ArcRadiusDimension2));
            model.PreUpdatePriority.Add(typeof(ElementLeaderDimension));
            model.PreUpdatePriority.Add(typeof(ComponentLeaderDimension));
            model.PreUpdatePriority.Add(typeof(DiameterDimension));
            model.PreUpdatePriority.Add(typeof(RemoteRadiusDimension));
            model.PreUpdatePriority.Add(typeof(DirectDistanceDimension));

            model.PreUpdatePriority.Add(typeof(OffsetFromMainBorderDimension));

            return model;
        }

        public static ContourModel CreateModel(List<IPrimitive> primitives)
        {
            List<IPrimitive> _primitives = DxfToModelPrimitiveAdapter.NormalizeContourPrimitives(primitives);

            ContourModel model = ContourElementFactory.CreateModel();
            IEndedPrimitive prevPrimitive = _primitives[0] as IEndedPrimitive;
            ContourElement prevElement = ContourElementFactory.CreateContourElementFromPrimitive(prevPrimitive, "");
            ContourElement firstElement = prevElement;
            ContourConnector connector = null;
            model.AddInnerComponent(firstElement);
            for(int i = 1; i < _primitives.Count; i++)
            {
                IEndedPrimitive primitive = _primitives[i] as IEndedPrimitive;
                ContourElement element = ContourElementFactory.CreateContourElementFromPrimitive(primitive, "");
                model.AddInnerComponent(element);
                connector = ContourElementFactory.CreateConnector(element.StartPoint);
                model.AddInnerComponent(connector);
                ContourConnectionProvider.Connect(prevElement, connector, EndType.End);
                ContourConnectionProvider.Connect(element, connector, EndType.Start);
                prevElement = element;
            }
            
            connector = ContourElementFactory.CreateConnector(firstElement.StartPoint);
            model.AddInnerComponent(connector);
            ContourConnectionProvider.Connect(prevElement, connector, EndType.End);
            ContourConnectionProvider.Connect(firstElement, connector, EndType.Start);

            return model;
        }

        public static ContourElement CreateLineContourElement(ModelPoint startPoint, ModelPoint endPoint, string name)
        {
            ContourElement element = ContourElementFactory.CreateContourElement(name);
            element.Primitive = new Line(startPoint, endPoint);
            return element;
        }

        public static ContourElement CreateCircleContourElement(ModelPoint startPoint, ModelPoint endPoint, double height, string name)
        {
            ContourElement element = ContourElementFactory.CreateContourElement(name);
            element.Primitive = new Circle(startPoint, endPoint, height);
            return element;
        }

        public static ContourElement CreateContourElementFromPrimitive(IPrimitive primitive, string name)
        {
            ContourElement element = ContourElementFactory.CreateContourElement(name);
            element.Primitive = primitive.Clone() as IPrimitive;
            return element;
        }

        private static ContourElement CreateContourElement(string name)
        {
            ContourElement element = new ContourElement();
            element.AcceptedTypesToConnect.Add(typeof(ContourConnector));

            element.AcceptedTypesToConnect.Add(typeof(Dimension));
            element.AcceptedTypesToConnect.Add(typeof(ContourElementDimension));
            element.AcceptedTypesToConnect.Add(typeof(ArcHeightDimension));
            element.AcceptedTypesToConnect.Add(typeof(ArcRadiusDimension));
            element.AcceptedTypesToConnect.Add(typeof(ArcRadiusDimension2));
            element.AcceptedTypesToConnect.Add(typeof(ElementLeaderDimension));
            element.AcceptedTypesToConnect.Add(typeof(ComponentLeaderDimension));
            element.AcceptedTypesToConnect.Add(typeof(RemoteRadiusDimension));


            element.ConnectPriorityForEnds.Add(typeof(ContourConnector));
            element.AddInnerComponent(new ContourElementHook(element, 0.2, ContourElementPoints.Start));
            element.AddInnerComponent(new ContourElementHook(element, 0.5, ContourElementPoints.Center));
            element.AddInnerComponent(new ContourElementHook(element, 0.8, ContourElementPoints.End));
            List<IContourConstraint> constraints = element.Constraints as List<IContourConstraint>;
            constraints.Add(new ConstraintClearElementInvalidModifiedFlag());
            element.Name = name;
            return element;
        }

        public static ContourConnector CreateConnector(ModelPoint position, int radiusInPixels)
        {
            ContourConnector connector = new ContourConnector();
            connector.RadiusInPixels = radiusInPixels;
            connector.MoveTo(position);

            connector.Tolerance = 10;
            connector.AcceptedTypesToConnect.Add(typeof(ContourElement));
            connector.AcceptedTypesToConnect.Add(typeof(ComponentLeaderDimension));
            connector.AcceptedTypesToConnect.Add(typeof(DirectDistanceDimension));

            List<IContourConstraint> constraints = connector.Constraints as List<IContourConstraint>;

            constraints.Add(new ConstraintRemoveInvalidConnectors());
            IContourConstraint joinEnds = new ConstraintJoinContourElementEnds(0);
            constraints.Add(joinEnds);
            return connector;
        }

        public static ContourConnector CreateConnector(ModelPoint position)
        {
            return ContourElementFactory.CreateConnector(position, GlobalData.ConnectorRadiusInPixels);
        }

        public static ContourText CreateText(string text, Font font, Color color)
        {
            ContourText contourText = new ContourText(text);
            contourText.Font = font;
            contourText.FontColor = color;
            return contourText;
        }

        public static ContourCommentLine CreateCommentLine(ModelPoint startPoint, ModelPoint endPoint)
        {
            ContourCommentLine line = new ContourCommentLine();
            line.Primitive = new Line(startPoint, endPoint);
            List<IContourConstraint> constraints = line.Constraints as List<IContourConstraint>;
            constraints.Add(new ConstraintStickCommentLineToSource());
            return line;
        }

        public static ContourHole CreateHole(ModelPoint position, double radius)
        {
            ContourHole hole = new ContourHole();
            ModelPoint startEndPoint = new ModelPoint(position.X, position.Y - radius);
            Circle firstCircleHalf = new Circle(new ModelPoint(position.X + radius, position.Y), new ModelPoint(position.X - radius, position.Y), radius);
            Circle secondCircleHalf = new Circle(new ModelPoint(position.X - radius, position.Y), new ModelPoint(position.X + radius, position.Y), radius);
            List<IPrimitive> primitives = new List<IPrimitive>();
            primitives.Add(firstCircleHalf);
            primitives.Add(secondCircleHalf);
            hole.Primitive = PrimitiveFactory.CreateCompositePrimitive(primitives);

            hole.AcceptedTypesToConnect.Add(typeof(DiameterDimension));
            hole.AcceptedTypesToConnect.Add(typeof(DirectDistanceDimension));
            hole.AcceptedTypesToConnect.Add(typeof(ComponentLeaderDimension));

            return hole;
        }

        public static ContourContainer CreateContainer(float rightIndent, float upIndent, float leftIndent, float downIndent)
        {
            ContourContainer container = new ContourContainer();
            container.HasBounds = false;
            container.AcceptedTypesToConnect.Add(typeof(OffsetFromMainBorderDimension));
            container.RightIndent = rightIndent;
            container.UpIndent = upIndent;
            container.LeftIndent = leftIndent;
            container.DownIndent = downIndent;
            return container;
        }

        public static ContourModel CreateAntoclockwiseModel(ContourModel model)
        {
            List<IPrimitive> primitives = new List<IPrimitive>();
            foreach (ContourElement element in model)
            {
                primitives.Add(element.Primitive);
            }
            primitives = DxfToModelPrimitiveAdapter.NormalizeContourPrimitives(primitives);
            ContourModel anticlockwiseModel = ContourElementFactory.CreateModel(primitives);
            return anticlockwiseModel;
        }
    }
}
